﻿using Blm.Utils.Extensions;
using Language.Resource;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Pipelines.Sockets.Unofficial.Arenas;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using VisionCloud.Core.Model.Ms;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Ms;
using VisionCloud.Domain.Extension;
using VisionCloud.Domain.Repository.Interface.Ms;
using VisionCloud.Utility;

namespace VisionCloud.Domain.Repository.Implement.Ms
{
    public class MsHouseMapRepository : BaseRepository<MsHouseMap>, IMsHouseMapRepository
    {
        private readonly IConfiguration cfg;

        public IStringLocalizer<RepositoryResource> Localizer { get; set; }

        public ILogger<MsHouseMapRepository> Logger { get; set; }

        private OperatorInfo tokenModel => OperatorProvider.Get();

        public MsHouseMapRepository(IConfiguration cfg)
        {
            this.cfg = cfg;
        }

        /// <summary>
        /// 根据户型图Id判断户型图是否已使用
        /// </summary>
        /// <param name="houseMapId">户型图Id</param>
        /// <returns></returns>
        public async Task<bool> IsHouseMapUsed(Guid houseMapId)
        {
            var result = await (from h in Db.MsHouseMaps
                                join j in Db.MsTasks on h.HouseMapId equals j.HouseMapId
                                where !h.IsDeleted && h.Status == MsHouseMap.EnumHouseMapStatus.启用 && !j.IsDeleted
                                      && h.HouseMapId == houseMapId
                                select new ProjectHouseMap()
                                {
                                    HouseMapId = h.HouseMapId,
                                    Name = h.Name,
                                    MapType = (int)h.MapType,
                                }).ToListAsync();
            if (result != null && result.Count > 0)
            {
                return true;
            }
            return false;
        }

        public async ValueTask<List<ProjectHouseMap>> GetHouseMapAsync(MsHouseMapCondition condition)
        {
            var list = await (
                     from j in Db.MsUserToProjects
                     join h in Db.MsHouseMaps on j.YunProjectGuid equals h.ProjectId
                     join p in Db.MsProjects on j.YunProjectGuid equals p.ProjectId
                     where (
                            (tokenModel.CompanyGuid == p.CompanyGuid && tokenModel.IsCompanyAdmin)
                           || j.YunUserGuid == tokenModel.Id || tokenModel.RoleCode.Contains("cloud_measure_robot_master")
                           )
                           && !h.IsDeleted && ((condition.ProjectId == Guid.Empty || condition.ProjectId == null) || h.ProjectId == condition.ProjectId) && (string.IsNullOrWhiteSpace(condition.Name) || h.Name.Contains(condition.Name)) && h.Status == MsHouseMap.EnumHouseMapStatus.启用
                           && ((!condition.HouseMapId.HasValue) || condition.HouseMapId == Guid.Empty || h.HouseMapId == condition.HouseMapId)
                           && (condition.ExcludeProjectId == Guid.Empty || p.ProjectId != condition.ExcludeProjectId)
                           && !p.IsDeleted && p.Status == MsProject.ProjectStatus.启用
                           //测量员和项目管理员只能到发布的户型信息。
                           && (!(tokenModel.RoleNameList.Contains("测量员") || tokenModel.RoleNameList.Contains("项目管理员"))
                           || (p.PubliceDate.HasValue && h.CreatedDate <= p.PubliceDate.Value))
                     select new ProjectHouseMap()
                     {
                         HouseMapId = h.HouseMapId,
                         Name = h.Name,
                         MapType = (int)h.MapType,
                         ProjectName = p.Name,
                         ProjectId = p.ProjectId,
                         MapArea = h.MapArea,
                         CreatedDate = h.CreatedDate,
                         LastUpdatedDate = h.LastUpdatedDate,
                         DataType = h.DataType ?? 0,
                         ImgSrc = h.Image_Base,
                         StandardMeasurePoints = h.MeasurePoints.ToObject<List<MeasurePoints>>()
                     })
                    .AsNoTracking()
               .OrderByDescending(s => s.LastUpdatedDate).DistinctBy(p => p.HouseMapId)
               .ToPageList(condition);

            foreach (var item in list)
            {
                item.ImgSrc = GetHouseMapImageUrl(item.DataType == 0 ? 0 : 1, item.ImgSrc);
            }

            return list.ToList();
        }

        public async Task<ApiPageResult> GetHouseMap(MsHouseMapCondition condition)
        {
            var list = await GetHouseMapAsync(condition);

            var result = condition.CopyTo<ApiPageResult>();
            result.PageData = list;
            return result;
        }

        public async Task<ApiPageResult> ExistedHouseMap(MsHouseMapCondition condition)
        {
            var list = await (
                     from j in Db.SysUserProject
                     join h in Db.MsHouseMaps on j.ProjectId equals h.ProjectId
                     join p in Db.MsProjects on j.ProjectId equals p.ProjectId
                     // 公司管理员可获取所有项目和户型
                     where (j.UserId == OperatorProvider.Get().Id || OperatorProvider.Get().RoleNameList.Contains("公司管理员"))
                           && !h.IsDeleted && ((condition.ProjectId == Guid.Empty || condition.ProjectId == null) || h.ProjectId == condition.ProjectId) && (String.IsNullOrWhiteSpace(condition.Name) || h.Name == condition.Name) && h.Status == MsHouseMap.EnumHouseMapStatus.启用
                           && !p.IsDeleted && p.Status == MsProject.ProjectStatus.启用
                     select new ProjectHouseMap()
                     {
                         HouseMapId = h.HouseMapId,
                         Name = h.Name,
                         MapType = (int)h.MapType,
                         ProjectName = p.Name,
                         ProjectId = p.ProjectId,
                         LastUpdatedDate = h.LastUpdatedDate,
                         DataType = h.DataType ?? 0,
                         ImgSrc = h.Image_Base,
                         StandardMeasurePoints = h.MeasurePoints.ToObject<List<MeasurePoints>>()
                     }).AsNoTracking()
               .OrderByDescending(s => s.LastUpdatedDate)
               .ToPageList(condition);
            var result = condition.CopyTo<ApiPageResult>();
            foreach (var item in list)
            {
                item.ImgSrc = GetHouseMapImageUrl(item.DataType == 0 ? 0 : 1, item.ImgSrc);
            }
            result.PageData = list;
            return result;
        }

        public async Task<string> GetHouseMapBase64(Guid houseMapId)
        {
            string strPath = string.Empty;

            var result = await (
                          from h in Db.MsHouseMaps
                          join i in Db.MsHouseMapPoints on h.HouseMapId equals i.HouseMapId
                          where h.Status == MsHouseMap.EnumHouseMapStatus.启用
                                && h.HouseMapId == houseMapId
                          select new MsHouseMapDto()
                          {
                              DataType = h.DataType ?? 0,
                              Image_Base = h.Image_Base
                          }).AsNoTracking().FirstOrDefaultAsync().ConfigureAwait(false);

            string path = GetHouseMapImagePath(result.DataType == 0 ? 0 : 1, result.Image_Base);
            if (System.IO.File.Exists(path))
            {
                FileInfo fi = new FileInfo(path);
                byte[] buff = new byte[fi.Length];
                FileStream fs = fi.OpenRead();
                fs.Read(buff, 0, Convert.ToInt32(fs.Length));
                fs.Close();
                strPath = "data:image/png;base64," + Convert.ToBase64String(buff);//byte[]转base64
            }

            return strPath;
        }

        /// <summary>
        /// 获取户型图Url
        /// </summary>
        /// <param name="dataType"></param>
        /// <param name="image_Base"></param>
        /// <returns></returns>
        /// <exception cref="OperationException"></exception>
        private string GetHouseMapImageUrl(int dataType, string image_Base) => dataType switch
        {
            0 => $"{cfg["FileServer"]}/{cfg["FileUploadFileMaster"]}/{cfg["FileUploadFileSon"]}/{image_Base}",
            1 => $"{cfg["FileServer"]}/algorithmreport{image_Base.Replace("algorithm-report", "")}",
            _ => throw new OperationException("暂不支持该类型的户型图")
        };

        /// <summary>
        /// 获取户型图存储的绝对路径
        /// </summary>
        /// <param name="dateType">0实测任务户型图，1机器人作业户型图</param>
        /// <param name="image_Base"></param>
        /// <returns></returns>
        /// <exception cref="OperationException"></exception>
        public string GetHouseMapImagePath(int dateType, string image_Base) => dateType switch
        {
            0 => FileInfoUtils.GetAbsolutePathFromRelative(cfg["FileUploadFileMaster"] + "/" + cfg["FileUploadFileSon"] + "/" + image_Base),
            1 => $"{cfg["AlgorithmReport"]}/{image_Base.Replace("algorithm-report", "")}",
            _ => throw new OperationException("暂不支持该类型的户型图")
        };

        /// <summary>
        /// 获取户型图信息
        /// </summary>
        /// <param name="houseMapId">户型图Id</param>
        /// <returns></returns>
        public async Task<MsHouseMapDto> GetHouseMap(Guid houseMapId)
        {
            var result = await (
                          from h in Db.MsHouseMaps
                          join p in Db.MsProjects on h.ProjectId equals p.ProjectId
                          where h.Status == MsHouseMap.EnumHouseMapStatus.启用
                                && h.HouseMapId == houseMapId
                          select new MsHouseMapDto()
                          {
                              HouseMapId = h.HouseMapId,
                              Name = h.Name,
                              MapType = (int)h.MapType,
                              Image_Base = h.Image_Base,
                              MeasurePoints = new List<MeasurePoints>(),
                              ProjectId = h.ProjectId,
                              HouseType = h.HouseType,
                              DataType = h.DataType ?? 0,
                              MapArea = h.MapArea,
                              HasMinus = h.HasMinus ?? true,
                              HasProjectPublished = p.PubliceDate.HasValue && p.PubliceDate > h.CreatedDate
                          }).AsNoTracking().FirstOrDefaultAsync().ConfigureAwait(false);

            // 测量站点和设计值
            if (result != null)
            {
                result.Image_Base = GetHouseMapImageUrl(result.DataType == 0 ? 0 : 1, result.Image_Base);
                result.HouseTypes = new KeyAndName[] { };
                if (!string.IsNullOrEmpty(result.HouseType))
                {
                    try
                    {
                        result.HouseTypes = JsonConvert.DeserializeObject<KeyAndName[]>(result.HouseType);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError($"{ex.Message}，{ex.StackTrace}", ex);
                        throw new OperationException(Localizer["YB0228"].Value);
                    }
                }
                var points = await (from h in Db.MsHouseMapPoints
                                    join p in Db.MsPointTypes on h.MsPointTypeId equals p.Id
                                    where h.HouseMapId == houseMapId && p.ProjectGuid == result.ProjectId
                                    orderby h.PointIndex ascending
                                    select new MeasurePoints()
                                    {
                                        PointId = h.PointId,
                                        pointIndex = h.PointIndex,
                                        x = h.x,
                                        y = h.y,
                                        offsetX = h.offsetX,
                                        offsetY = h.offsetY,
                                        Type = (int)p.Type,
                                        TypeName = p.Name,
                                        HouseTypeName = h.HouseTypeName,
                                        HouseTypeKey = h.HouseTypeKey
                                    }).AsNoTracking().ToListAsync().ConfigureAwait(false);

                result.MeasurePoints = points;
                if (result.MeasurePoints == null || result.MeasurePoints.Count == 0)
                {
                    return result;
                }

                var taskMeasureItems = await Db.MsMeasureItems.Join(Db.MsMeasureTemplates,
                    x => new { x.HouseMapId, x.Id },
                    y => new { y.HouseMapId, y.Id },
                    (x, y) => new { x.HouseMapId, x.PointId, x.Title, x.Id, x.Unit, x.Code, y.Type, y.DesignValues }
                    ).Where(z => z.HouseMapId == houseMapId)
                    .Select(m => new MeasureItem
                    {
                        HouseMapId = m.HouseMapId.ToString(),
                        PointId = m.PointId.ToString(),
                        Title = m.Title,
                        Id = m.Id,
                        Unit = m.Unit,
                        Code = m.Code,
                        DisplayValue = $"{m.Id}:",
                        Type = m.Type ?? (m.Code == "r-kaijianjinshenjicha" ? 1 : 0),
                        DesignValues = !string.IsNullOrWhiteSpace(m.DesignValues) ? m.DesignValues.ToObject<List<DesignValue>>() : new List<DesignValue>()
                    })
                    .AsNoTracking().OrderBy(x => x.Type).ThenBy(x => x.Title).ToListAsync().ConfigureAwait(false);

                foreach (var item in result.MeasurePoints)
                {
                    if (item == null)
                    {
                        continue;
                    }
                    item.MeasureItems = taskMeasureItems.Where(p => p.PointId == item.PointId.ToString()).ToList();
                    if (item.MeasureItems != null)
                    {
                        foreach (var mi in item.MeasureItems)
                        {
                            string value = string.Empty;
                            foreach (var des in mi.DesignValues)
                            {
                                value += $"{des.Title}:{(int)des.Value},";
                            }
                            if (!string.IsNullOrWhiteSpace(value))
                            {
                                mi.DisplayValue += value.Substring(0, value.Length - 1);
                            }
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 获取户型图
        /// </summary>
        /// <param name="houseMapId">户型图Id</param>
        /// <returns></returns>
        public async ValueTask<string> GetHouseMapBase(Guid houseMapId)
        {
            var result = await (
                          from h in Db.MsHouseMaps
                          where h.Status == MsHouseMap.EnumHouseMapStatus.启用
                                && h.HouseMapId == houseMapId
                          select new MsHouseMapDto()
                          {
                              DataType = h.DataType ?? 0,
                              Image_Base = h.Image_Base
                          }).AsNoTracking().FirstOrDefaultAsync().ConfigureAwait(false);

            return GetHouseMapImageUrl(result.DataType == 0 ? 0 : 1, result.Image_Base);
        }

        /// <summary>
        /// 获取户型图Url
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public async ValueTask<string> GetHouseMapBaseUrl(Guid taskId)
        {
            var result = await Db.CSMSTasks.Join(Db.MsHouseMaps, a => a.HouseMapId, b => b.HouseMapId, (a, b) => new { a.TaskId, b.Image_Base, DataType = b.DataType ?? 0 })
                                 .Where(x => x.TaskId == taskId).FirstOrDefaultAsync();

            return GetHouseMapImageUrl(result.DataType == 0 ? 0 : 1, result.Image_Base);
        }

        /// <summary>
        /// 根据站点类型获取户型图信息
        /// </summary>
        /// <param name="condition">查询条件</param>
        /// <returns></returns>
        public async Task<List<MsHouseMap>> GetHouseMapByPointType(HouseMapCondition condition)
        {
            var result = await (
              from h in Db.MsHouseMaps
              join p in Db.MsHouseMapPoints on h.HouseMapId equals p.HouseMapId
              where !h.IsDeleted && h.Status == MsHouseMap.EnumHouseMapStatus.启用
                                 && p.MsPointTypeId == condition.MsPointTypeId
                                 && (condition.ProjectGuid.HasValue ? (h.ProjectId == condition.ProjectGuid.Value) : true)
              select h).ToListAsync();
            return result;
        }

        public async Task<List<MsHouseMap>> GetDataByProjects(Guid[] projectIds)
        {
            var result = await (
               from h in Db.MsHouseMaps
               where projectIds.Contains(h.ProjectId) && h.IsDeleted == false
               select h).AsNoTracking().ToListAsync().ConfigureAwait(false);
            return result;
        }

        /// <summary>
        /// 根据任务Id获取户型图信息
        /// </summary>
        /// <param name="projectId">项目id</param>
        /// <returns></returns>
        public async Task<List<MsHouseMap>> GetHouseMapByProject(Guid projectId)
        {
            var result = await (
                from h in Db.MsHouseMaps
                join p in Db.MsHouseMapPoints on h.HouseMapId equals p.HouseMapId
                where !h.IsDeleted && h.Status == MsHouseMap.EnumHouseMapStatus.启用
                                   && h.ProjectId == projectId
                select h).ToListAsync();
            return result;
        }

        /// <summary>
        /// 检查户型图对应的项目是否已发布
        /// </summary>
        /// <param name="houseMapId"></param>
        /// <returns></returns>
        public async Task<bool> HouseMapForProjectIsPublice(Guid houseMapId)
        {
            var result = await (from h in Db.MsHouseMaps
                                join p in Db.MsProjects on h.ProjectId equals p.ProjectId
                                where h.HouseMapId == houseMapId
                                select new
                                {
                                    h.HouseMapId,
                                    h.ProjectId,
                                    h.CreatedDate,
                                    p.PubliceDate,
                                }).FirstOrDefaultAsync();
            if (result == null)
                return true;
            if (result.PubliceDate.HasValue && result.PubliceDate.Value > result.CreatedDate)
                return true;
            return false;
        }

        /// <summary>
        /// 获取户型图对应的站点类型
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="houseMapId"></param>
        /// <returns></returns>
        public async ValueTask<List<MeasurePoints>> GetPointTypes(Guid projectId, Guid houseMapId)
        {
            var list = await (from h in Db.MsHouseMapPoints
                              join u in Db.MsPointTypes on new { id = h.MsPointTypeId, projectID = projectId } equals new
                              {
                                  id = u.Id,
                                  projectID = u.ProjectGuid
                              }
                              where h.HouseMapId == houseMapId
                              select new MeasurePoints
                              {
                                  PointId = h.PointId,
                                  pointIndex = h.PointIndex,
                                  x = h.x,
                                  y = h.y,
                                  offsetX = h.offsetX,
                                  offsetY = h.offsetY,
                                  Type = u.Type,
                                  TypeId = u.Id,
                                  TypeName = u.Name,
                              }).AsNoTracking().OrderBy(x => x.pointIndex).ToListAsync();

            return list;
        }
    }
}